#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <types.h>
#include <utils.h>
#include <unistd.h>
#include <syserr.h>
#include <zlib.h>
#include <printk.h>
#include <threadpool.h>
#include "fs.h"
#include "sczip.h"

int gzcompress(const char *src, int len, char *dst, int maxlen)
{
    z_stream c_stream;
    int err = 0;
    int windowBits = 15;
    int GZIP_ENCODING = 16;

    if (src && len > 0)
    {
        c_stream.zalloc = NULL;
        c_stream.zfree  = NULL;
        c_stream.opaque = NULL;
        if (deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
                         windowBits | GZIP_ENCODING, 8, Z_DEFAULT_STRATEGY)
            != Z_OK)
            return -1;
        c_stream.next_in  = (Bytef *)src;
        c_stream.avail_in = len;
        c_stream.next_out = (Bytef *)dst;
        c_stream.avail_out = maxlen;
        while (c_stream.avail_in != 0 && c_stream.total_out < maxlen)
        {
            if (deflate(&c_stream, Z_NO_FLUSH) != Z_OK) return -1;
        }
        if (c_stream.avail_in != 0) return c_stream.avail_in;
        for (;;)
        {
            if ((err = deflate(&c_stream, Z_FINISH)) == Z_STREAM_END)
                break;
            if (err != Z_OK)
                return -1;
        }
        if (deflateEnd(&c_stream) != Z_OK)
            return -1;
        return c_stream.total_out;
    }
    return -1;
}

int gzdecompress(const char *src, int len, const char *dst, int maxlen)
{
    z_stream strm;

    strm.zalloc = NULL;
    strm.zfree  = NULL;
    strm.opaque = NULL;

    strm.avail_in  = len;
    strm.avail_out = maxlen;
    strm.next_in   = (Bytef *)src;
    strm.next_out  = (Bytef *)dst;

    int err = -1;
    err = inflateInit2(&strm, MAX_WBITS+16);
    if (err == Z_OK)
    {
        err = inflate(&strm, Z_FINISH);
        if (err == Z_STREAM_END)
        {
            err = strm.total_out;
        }
        else
        {
            inflateEnd(&strm);
            return err;
        }
    }
    else
    {
        inflateEnd(&strm);
        return err;
    }
    inflateEnd(&strm);
    return err;
}

int zip_file(const char *srcpath, const char *dstpath)
{
    FILE *fr, *fw;
    char *dr, *dw;
    int r, size;

    fr = fopen(srcpath, "rb");
    fw = fopen(dstpath, "wb");
    if (!fr || !fw)
    {
        r = -EBADF;
        goto close_exit;
    }

    fseek(fr, 0, SEEK_END);
    size = ftell(fr);
    dr = kmalloc(size);
    dw = kmalloc(size);
    if (!dr || !dw)
    {
        r = -ENOMEM;
        goto free_exit;
    }

    fseek(fr, 0, SEEK_SET);
    r = fread(dr, 1, size, fr);
    assert(r == size);

    r = gzcompress(dr, size, dw, size);
    if (r > 0)
        fwrite(dw, 1, r, fw);

free_exit:
    if (dr) kfree(dr);
    if (dw) kfree(dw);

close_exit:
    if (fr) fclose(fr);
    if (fw) fclose(fw);
    return r;
}

static svc_work_t work_req;
struct zip_work
{
    const char *src;
    const char *dst;
    pthread_mutex_t *lock;
};
static void zip_work_cleanup(struct zip_work *zw)
{
    if (zw->src) kfree((void *)zw->src);
    if (zw->dst) kfree((void *)zw->dst);
    if (zw) kfree(zw);
}
static void zip_work_cb(svc_work_t *req)
{
    struct zip_work *zw = req->data;
    ev_fs_t fs;
    int r;

    pthread_mutex_lock(zw->lock);
    zip_file(zw->src, zw->dst);
    pthread_mutex_unlock(zw->lock);

    r = uv_fs_unlink(req->loop, &fs, zw->src, NULL);
    if (r != 0)
        APP_PE("<zip_work_cb>: remove [%s] fault!, r = %d\n", zw->src, r);
    uv_fs_req_cleanup(&fs);
    zip_work_cleanup(zw);
}
int zip_file_worker(const char *srcpath, const char *dstpath,
                    pthread_mutex_t *lock)
{
    struct zip_work *zw = kmalloc(sizeof(struct zip_work));
    if (!zw) return -ENOMEM;

    zw->src = svc__strdup(srcpath);
    zw->dst = svc__strdup(dstpath);
    if (!zw->src || !zw->dst)
        goto err;

    zw->lock = lock;
    work_req.data = zw;
    svc_queue_work(ev_default_loop(), &work_req, zip_work_cb, NULL);
    return 0;

err:
    zip_work_cleanup(zw);
    return -ENOMEM;
}
